use super::{
    eth::EthApiImpl,
    middle::{self, request_middle},
    net::NetApiImpl,
    web3::Web3ApiImpl,
};
use crate::ledger::State;
use ruc::*;
use std::{net::SocketAddr, thread, thread::available_parallelism};
use web3_rpc_core::{EthApi, NetApi, Web3Api};

pub struct Web3ServerBuilder {
    pub upstream: String,
    pub upstream_is_unix_sock: bool,
    pub http: Option<String>,
    pub ws: Option<String>,
    pub state: State,
}

impl Web3ServerBuilder {
    fn build_http(&self) -> Option<jsonrpc_http_server::Server> {
        if let Some(http) = self.http.as_ref() {
            let mut io = jsonrpc_core::IoHandler::new();

            let eth = EthApiImpl {
                upstream: self.upstream.clone(),
                upstream_is_unix_sock: self.upstream_is_unix_sock,
                state: self.state.clone(),
            };

            let net = NetApiImpl {};
            let web3 = Web3ApiImpl {};

            io.extend_with(eth.to_delegate());
            io.extend_with(net.to_delegate());
            io.extend_with(web3.to_delegate());

            pnk!(middle::init_upstream(
                &self.upstream,
                self.upstream_is_unix_sock
            ));

            let addr = pnk!(http.parse::<SocketAddr>());
            let s = jsonrpc_http_server::ServerBuilder::new(io.clone())
                .request_middleware(request_middle)
                .threads(available_parallelism().map(usize::from).unwrap_or(4))
                .keep_alive(false)
                .start_http(&addr)
                .expect("failed to create http server");

            Some(s)
        } else {
            None
        }
    }

    fn build_ws(&self) -> Option<jsonrpc_ws_server::Server> {
        if let Some(ws) = self.ws.as_ref() {
            let mut io = jsonrpc_core::IoHandler::new();

            let eth = EthApiImpl {
                upstream: self.upstream.clone(),
                upstream_is_unix_sock: self.upstream_is_unix_sock,
                state: self.state.clone(),
            };

            let net = NetApiImpl {};
            let web3 = Web3ApiImpl {};

            io.extend_with(eth.to_delegate());
            io.extend_with(net.to_delegate());
            io.extend_with(web3.to_delegate());

            let addr = pnk!(ws.parse::<SocketAddr>());
            let s = jsonrpc_ws_server::ServerBuilder::new(io.clone())
                .start(&addr)
                .expect("failed to create http server");

            Some(s)
        } else {
            None
        }
    }

    pub fn build(self) -> Web3Server {
        let http = self.build_http();
        let ws = self.build_ws();
        Web3Server { http, ws }
    }
}

pub struct Web3Server {
    http: Option<jsonrpc_http_server::Server>,
    ws: Option<jsonrpc_ws_server::Server>,
}

impl Web3Server {
    pub fn start(mut self) {
        if let Some(http) = self.http.take() {
            println!("*** Web3-http serve at {} ***", http.address());
            thread::spawn(|| {
                http.wait();
            });
        } else {
            println!("*** Web3-http service is not registered ***");
        };

        if let Some(ws) = self.ws {
            println!("*** Web3-websocket serve at {} ***", ws.addr());
            thread::spawn(|| {
                pnk!(ws.wait());
            });
        } else {
            println!("*** Web3-websocket service is not registered ***");
        };
    }
}
